#Python library imports
import re
#External libraries imports
#Internal imports
from Exceptions import UndefinedNameException, RedeclarationException, RedefinitionException, ValueTypeException

__all__ = ("FSPNamespace", "typeMappings")

typeMappings =\
{
  "const": int,
  "range": range,
  "par": int,
  "var": list,
  "set": list,
  "label": str
}

class FSPNamespace(object):
  def __init__(self, name="anonymous", father=None):
    self._table = dict()
    self._father = father
    self._children = []
    self.name = (self._father.name + "::" if self._father else "") + name
    if father: father.addChild(self)
  def addName(self, source, line, name, value=None, FSPType=None):
    if name not in self._table.keys():
      self._table[name] = None
      if FSPType:
        self.bindName(source, line, name, value, FSPType)
      return name
    else:
      raise RedeclarationException(source, line, name)
  def bindName(self, source, line, name, value, FSPType):
    if name in self._table.keys():
      if type(value) == typeMappings[FSPType]:
        self._table[name] = {"FSPType": FSPType, "value": value}
        return [name, value]
      else:
        raise ValueTypeException(source, line, FSPType)
    else:
      raise UndefinedNameException(source, line, name)
  def addObject(self, source, line, name, value, FSPType):
    return [self.addName(source, line, name, value, FSPType), value]
  def getName(self, source, line, name, *expectedTypes):
    if name in self._table.keys():
      if self._table[name] and self._table[name]["FSPType"] in [e for e in expectedTypes]:
        return self._table[name]["value"]
      else:
        raise ValueTypeException(source, line, str(expectedTypes))
    elif self._father != None:
      return self._father.getName(source, line, name, *expectedTypes)
    else:
      raise UndefinedNameException(source, line, name)
  def getView(self, nameFilter=".*", types="*", depth=0):
    if types == "*":
      types = typeMappings.keys()
    view = {n: self._table[n]["value"] for n in self._table.keys()
      if re.match(nameFilter, n) and self._table[n]["FSPType"] in types}
    if depth > 0 and self._father:
      fatherView = self._father.getView(nameFilter, types, depth-1)
      for k in fatherView.keys():
        if k not in view:
          view[k] = fatherView[k]
    return view
  def addChild(self, child):
      self._children.append(child)
  def testName(self, name):
    return name in self._table
  def keys(): return self._table.keys()
  def __repr__(self): return self._table.__repr__()
  def __str__(self): return self._table.__str__()
  def prettyPrint(self):
    root = self
    while root._father: root = root._father #Il padre dei padri
    def recursivePrint(node, indentationLevel):
      print("\t"*indentationLevel, node._table)
      for c in node._children: recursivePrint(c, indentationLevel + 1)
    recursivePrint(root, 0)
